import * as shlib from 'shlib/browser'

import { ShTerm } from './index'
import type { ShTextAttribute } from './index'

import { ShElement } from './ShElement'


export class ShSpanElement extends ShElement {

    public static create($term: ShTerm, text: string): ShSpanElement {
        // 创建文本段元素对象
        const $span = shlib.createElement('shterm-span') as ShSpanElement

        // 将文本段的文本属性设为终端当前文本属性
        const ta = $term.currTextAttr
        $span.updateCssStyle({
            backgroundColor: ta.backColor,
            color: ta.foreColor,
            fontWeight: ta.bold ? 'bold' : '',
            fontStyle: ta.italic ? 'italic' : '',
            textDecoration: ta.underline ? 'underline' : '',
        })

        // 设置文本段文本，同时更新文本段的宽度和 'w' 属性
        $span.setText(text, $term)

        return $span
    }

    constructor() {
        super()
    }

    public countColumns(): number {
        if (this.innerText.length === 0)
            return 0

        if (this.hasAttribute('w'))
            return this.innerText.length * 2
        else
            return this.innerText.length
    }

    public columnToCharIndex(col: number): number {        
        const index = this.hasAttribute('w') ? col / 2 : col
        const lastIndex = this.innerText.length - 1
        return Math.floor(index) > lastIndex ? lastIndex - index : index
    }

    /**
     * 与另一个文本段或者文本属性对象比较两者文本属性是否相等。
     */
    public compareTextAttribute(other: ShSpanElement | ShTextAttribute): boolean {
        const s1 = this.style

        if (other instanceof ShSpanElement) {
            const s2 = other.style

            if (this.hasAttribute('w') !== other.hasAttribute('w'))
                return false
    
            if (s1.backgroundColor !== s2.backgroundColor)
                return false
    
            if (s1.color !== s2.color)
                return false
    
            if (s1.fontWeight !== s2.fontWeight)
                return false
    
            if (s1.fontStyle !== s2.fontStyle)
                return false
    
            if (s1.textDecoration !== s2.textDecoration)
                return false
        }
        else {
            if (s1.backgroundColor !== other.backColor)
                return false
    
            if (s1.color !== other.foreColor)
                return false
    
            if (s1.fontWeight !== (other.bold ? '' : 'bold'))
                return false

            if (s1.fontStyle !== (other.italic ? '' : 'italic'))
                return false

            if (s1.textDecoration !== (other.underline ? '' : 'underline'))
                return false
        }

        return true
    }

    public setText(text: string, $term?: ShTerm | null) {
        this.innerText = text
        this.updateLayout($term)
    }

    /**
     * 根据当前文本内容和文本属性，更新文本段宽度和 'w' 属性。
     * 
     * 如果当前文本段元素还未插入到 DOM，则需要指定 ShTerm 示例，否则可以不用指定。
     */
    public updateLayout($term?: ShTerm | null) {
        // 如果没有指定 $term，则寻找父节点上挂载的 ShTerm 对象
        $term = $term || this.$term()
        shlib.assertNotNull($term)

        // 计算文本宽度，设置 'w' 属性
        if (this.innerText.length > 0) {
            const cw = $term.font.charWidth(this.innerText[0]!, $term.currTextAttr.bold)
            const cellPerChar = (cw <= $term.cellWidth) ? 1 : 2
            this.style.width = `${this.innerText.length * $term.cellWidth * cellPerChar}px`
            this.toggleAttribute('w', cellPerChar === 2)
        } else {
            this.style.width = '0px'
            this.toggleAttribute('w', false)
        }
    }

    /**
     * 将当前文本段在中间断开，分裂成两个文本段，使得左侧文本段所占列数不超过 maxCol（如果希望断开位置在宽字符
     * 中间，那么实际断开位置会是 maxCol - 1）。
     * 
     * 断开后，返回断开位置右侧的文本段对象。如果断开位置超出了文本段的列数，则不发生断开，返回 null。如果断开
     * 位置在文本段的开始位置，则直接返回当前文本段对象。
     * 
     * @param maxCol 分裂后，左侧的文本段可能的最大列数
     */
    public breakAt(maxCol: number): ShSpanElement | null {
        if (maxCol >= this.countColumns())
            return null

        const index = Math.floor(this.columnToCharIndex(maxCol))
        if (index === 0)
            return this

        const text = this.innerText
        this.setText(text.slice(0, index))

        const $span = this.cloneNode(true) as ShSpanElement
        this.insertAdjacentElement('afterend', $span)
        $span.setText(text.slice(index))
        
        return $span
    }

    /**
     * 尝试将当前文本段元素和其右侧的文本段元素合并。如果不能合并，没有任何动作发生；如何能够合
     * 并，右侧文本段将被删除，其文字内容追加到本文本段的尾部。
     * 
     * 两个文本段能合并的条件是两者的文本属性完全相同。
     */
    public tryMergeRight() {
        const $rightSpan = this.nextElementSibling as ShSpanElement | null
        if ($rightSpan && this.compareTextAttribute($rightSpan)) {
            shlib.assertInstanceOf($rightSpan, ShSpanElement)
            $rightSpan.remove()
            this.setText(this.innerText + $rightSpan.innerText)    
        }
    }
}

customElements.define('shterm-span', ShSpanElement)
